﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using KC.Framework.Base;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

namespace KC.Storage.BlobService
{
    internal partial class AzureReader
    {
        public override bool ExistBlob(string containerName, string blobId)
        {
            if (string.IsNullOrEmpty(blobId))
                return false;

            CloudBlobContainer container = this.Client.GetContainerReference(containerName);
            if (!this.DoesContainerExist(container))
                return false;

            string realBlobId = GetActualBlobId(blobId);
            CloudBlob blob = container.GetBlobReference(realBlobId);
            try
            {
                blob.FetchAttributesAsync();
                return true;
            }
            catch (StorageException)
            {
                throw;
            }
        }

        public override BlobInfo GetBlob(string tenant, string encryptionKey, bool isInternal, string userId, string container, string blobId, bool useCacheIfAvailable)
        {
            if (string.IsNullOrEmpty(blobId) || string.IsNullOrEmpty(container)) return null;

            var sw = new Stopwatch();
            sw.Start();

            int offset = -1;
            int length = -1;
            string realBlobId = GetActualBlobIdAndOffset(blobId, out offset, out length);
            if (useCacheIfAvailable && this.Cache.Exists(container, realBlobId))
            {
                BlobInfo bi = this.Cache.GetBlob(tenant, encryptionKey, isInternal, userId, container, realBlobId);

                //LogUtil.LogDebug("Retrieve Blob from local store", blobId, sw.ElapsedMilliseconds);

                if (offset != -1)
                {
                    bi = GetBlobSegment(bi, offset, length, blobId);
                }
                return bi;
            }
            else
            {
                CloudBlob blob = this.Client.GetContainerReference(container).GetBlobReference(realBlobId);
                if (blob != null)
                {
                    byte[] cacheData, cacheMetadata;
                    BlobInfo bi = GetBlob(tenant, encryptionKey, isInternal, userId, container, realBlobId, useCacheIfAvailable, out cacheData, out cacheMetadata);
                    //LogUtil.LogDebug("Retrieve Blob", blobId, sw.ElapsedMilliseconds);

                    if (useCacheIfAvailable && cacheData != null)
                    {
                        //this.Cache.Insert(container, realBlobId, cacheData, cacheMetadata);
                    }
                    if (offset != -1)
                    {
                        bi = GetBlobSegment(bi, offset, length, blobId);
                    }
                    return bi;
                }
                return null;
            }
        }

        public override DateTime GetBlobLastModifiedTime(string containerName, string blobId)
        {
            try
            {
                CloudBlob blob = this.GetBlob(containerName, blobId);
                blob.FetchAttributesAsync();
                return blob.Properties.LastModified.HasValue 
                    ? blob.Properties.LastModified.Value.DateTime 
                    : DateTime.MinValue;
            }
            catch
            {
                return DateTime.MinValue;
            }
        }

        public override Dictionary<string, string> GetBlobMetadata(string containerName, string blobId)
        {
            CloudBlob blob = this.GetBlob(containerName, blobId);
            return GetBlobMetadata(blob).Result;
        }

        public override BlobInfo GetBlobWithoutEncryption(string containerName, string blobId, bool useCacheIfAvailable)
        {
            if (string.IsNullOrEmpty(blobId) || string.IsNullOrEmpty(containerName)) return null;

            var sw = new Stopwatch();
            sw.Start();

            int offset = -1;
            int length = -1;
            string realBlobId = GetActualBlobIdAndOffset(blobId, out offset, out length);
            if (useCacheIfAvailable && this.Cache.Exists(containerName, realBlobId))
            {
                BlobInfo bi = this.Cache.GetBlobWithoutEncryption(containerName, realBlobId);
                //LogUtil.LogDebug("Retrieve Blob from local store", blobId, sw.ElapsedMilliseconds);
                if (offset != -1)
                {
                    bi = GetBlobSegment(bi, offset, length, blobId);
                }
                return bi;
            }
            else
            {
                CloudBlob blob = GetBlob(containerName, realBlobId);
                if (blob != null)
                {
                    string type = (blob.Metadata != null && blob.Metadata["Type"] != null) ? blob.Metadata["Type"] : "Unknown";
                    string format = (blob.Metadata != null && blob.Metadata["Format"] != null) ? blob.Metadata["Format"] : "Unknown";
                    string fileName = (blob.Metadata != null && blob.Metadata["FileName"] != null) ? blob.Metadata["FileName"] : "Unknown";

                    blob.FetchAttributesAsync();
                    var fileByteLength = blob.Properties.Length;
                    var blobData = new byte[fileByteLength];
                    blob.DownloadToByteArrayAsync(blobData, 0);
                    BlobInfo blobInfo = new BlobInfo(realBlobId, type, format, fileName, blobData) { Size = blobData.Length };
                    //LogUtil.LogDebug("Retrieve Blob", blobId, sw.ElapsedMilliseconds);

                    if (useCacheIfAvailable)
                    {
                        this.Cache.InsertWithoutEncryption(containerName, blobId, blobInfo.Data);
                    }
                    if (offset != -1)
                    {
                        blobInfo = GetBlobSegment(blobInfo, offset, length, blobId);
                    }
                    return blobInfo;
                }
                return null;
            }
        }

        public List<BlobInfo> GetBlobs(string tenant, string encryptionKey, bool isInternal, string userId, string container, List<string> blobIds, bool returnPlaceholderIfNotFound, bool isUserLevel = false)
        {
            List<BlobInfo> blobInfos = new List<BlobInfo>();
            foreach (string blobId in blobIds)
            {
                int offset = -1;
                int length = -1;
                string realBlobId = GetActualBlobIdAndOffset(blobId, out offset, out length);

                byte[] cacheData;
                byte[] cacheMetadata;
                BlobInfo bi = GetBlob(tenant, encryptionKey, isInternal, userId, container, realBlobId, false, out cacheData, out cacheMetadata);
                if (bi != null)
                {
                    if (offset != -1)
                    {
                        bi = GetBlobSegment(bi, offset, length, blobId);
                    }
                    blobInfos.Add(bi);
                }
            }
            return blobInfos;
        }
    }
}
